/*
 * Copyright (c) 2006 Sun Microsystems, Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to 
 * deal in the Software without restriction, including without limitation the 
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 
 * sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 **/

package org.sunspotworld.demo;

import org.sunspotworld.util.PrettyPrint;

import com.sun.spot.peripheral.*;
import com.sun.spot.peripheral.radio.*;
import com.sun.spot.peripheral.ota.*;
import com.sun.spot.peripheral.radio.mhrp.aodv.Constants;

import com.sun.spot.io.j2me.radio.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.Utils;
import com.sun.spot.util.Queue;

import java.io.*;
import javax.microedition.io.*;


/**
 * A simple packet sniffer. Listens to all packets received by the radio
 * and prints out the packets seen. 
 *
 * This application requires that the SPOT it is on does not start up any
 * services that use the radio, e.g. OTA mode must be disabled.  It also
 * must be run on a SPOT, not as a host app, since it needs to access the
 * physical layer of the radio stack.
 * 
 * Note: while in promiscuous listening mode the radio chip will not send
 * out any ACKs, so any Spot sending us a packet will never be notified it
 * was delivered---the sender will get a No ACK exception.
 *
 * Author: Ron Goldman
 * Release: Orange after 11-30-06
 */
public class PacketSniffer {
    
    private static IProprietaryRadio radio = Spot.getInstance().getIProprietaryRadio();
    private static I802_15_4_PHY i802phyRadio = Spot.getInstance().getI802_15_4_PHY();

    private Queue printQueue = new Queue();
    private Queue freeQueue = new Queue();
    
    private static final int PREALLOCATE = 500;
    private static int allocPkts = 0;
    
    public PacketSniffer(Queue printQueue, Queue freeQueue) {
        this.printQueue = printQueue;
        this.freeQueue = freeQueue;
        setupRadio();
    }

    /*
     * Note: cannot use MAC layer since API does not support promiscuous mode.
     */
    private void setupRadio() {
        radio.reset();
        int channel = Utils.getSystemProperty("radio.channel", Utils.getManifestProperty("DefaultChannelNumber", 26));
        System.out.println("  Radio set to channel " + channel);
        i802phyRadio.plmeSet(I802_15_4_PHY.PHY_CURRENT_CHANNEL, channel);
        int result = i802phyRadio.plmeSetTrxState(I802_15_4_PHY.RX_ON);
        if (I802_15_4_PHY.SUCCESS != result) {
            throw new SpotFatalException("State transition from trx_off to rx_on failed");
        }
    }

    public void readPackets() {
        for (int i = 0; i < PREALLOCATE; i++) {
            freeQueue.put(RadioPacket.getDataPacket());
        }
        System.out.println("Listening for packets....");
        int overflowCnt = radio.getRxOverflow();
        while (true) {
            RadioPacket rp = null;
            try {
                if (freeQueue.isEmpty()) {
                    rp = RadioPacket.getDataPacket();
                    allocPkts++;
                } else {
                    rp = (RadioPacket)freeQueue.get();
                }
                i802phyRadio.pdDataIndication(rp);  // receive new packet
                rp.decodeFrameControl();
                if (rp.isAck()) {
                    printQueue.put(rp);
                } else {
                    LowPanPacket lpp = new LowPanPacket(rp);
                    printQueue.put(lpp);
                }
                int o = radio.getRxOverflow();
                if (o > overflowCnt) {
                    overflowCnt = o;
                    System.out.println("--- radio buffer overflow: " + overflowCnt);
                }
            } catch (Exception ex) {
                System.out.println("Error in receive loop: " + ex);
                if (rp != null) {
                    if (rp.isAck()) {
                        System.out.println("  while processing an ACK packet");
                    } else {
                        System.out.println("  while processing packet from " + Long.toString(rp.getSourceAddress(), 16) +
                                " to " + Long.toString(rp.getDestinationAddress(), 16));
                        byte buffer[] = new byte[rp.getMACPayloadLength()];
                        for (int i = 0; i < rp.getMACPayloadLength(); i++) {
                            buffer[i] = rp.getMACPayloadAt(i);
                        }
                        System.out.println(PrettyPrint.prettyPrint(buffer));
                    }
                }
                ex.printStackTrace();
            }
        }
    }

    public static int getAllocPkts() {
        return allocPkts;
    }


}
